<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */
namespace app\service\batch\kuaishou\schema;

use app\enum\ConfigEnum;
use app\enum\KuaiShouEnum;
use app\model\BatchCreateSchemaUriModel;
use app\model\ClientAccountModel;
use app\model\KsAdvertiserSchemaCodeModel;
use app\model\mongo\MongoKsSchemaVideoModel;
use app\model\mongo\MongoKsUserVideoModel;
use app\service\client\AccountService;
use app\service\client\KuaiShouAccountService;
use MongoDB\BSON\Regex;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Cache;
use think\facade\Db;

/**
 * 直达链接视频服务.
 */
class SchemaVideoService extends SchemaService
{
    protected const BATCH_KEY = ConfigEnum::CACHE_CONFIG[KuaiShouEnum::BATCH_CONFIG_LOCK_KEY]['schema_video'];

    /**
     * @var null|batchCreateSchemaUriModel 直达链接模型
     */
    private ?BatchCreateSchemaUriModel $uriModel;

    /**
     * @var string userId
     */
    private string $advUserId;

    /**
     * @var string 素材ID
     */
    private string $materialId;

    /**
     * @var null|ClientAccountModel 账户信息
     */
    private ?ClientAccountModel $account;

    /**
     * @var null|MongoKsSchemaVideoModel 关系模型
     */
    private ?MongoKsSchemaVideoModel $schema;

    /**
     * @var null|MongoKsUserVideoModel 视频模型
     */
    private ?MongoKsUserVideoModel $videoModel;

    /**
     * 运行设置视频.
     * @param mixed $objectId
     * @throws ModelNotFoundException
     * @throws DbException
     * @throws DataNotFoundException
     */
    public function setMaterial(string $advId = '', $objectId = ''): bool
    {
        if (! $this->setMaterialVideo($advId, $objectId)) {
            return false;
        }

        if (! self::setSchemaVideo()) {
            return false;
        }

        return true;
    }

    /**
     * 设置直达链接是否已使用.
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function setSchemaUse(): bool
    {
        $list = (new KsAdvertiserSchemaCodeModel())
            ->where('type', KuaiShouEnum::SCHEMA_VIDEO)
            ->limit(20)
            ->select();

        if (count($list) <= 0) {
            return self::setError('数据为空');
        }

        foreach ($list as $model) {
            $obj = (new BatchCreateSchemaUriModel())
                ->where('advertiser_id', $model->getAttr('advertiser_id'))
                ->where('app_id', $model->getAttr('app_id'))
                ->where('material_id', $model->getAttr('material_id'))
                ->where('type', KuaiShouEnum::SCHEMA_VIDEO)
                ->find();

            if ($obj instanceof BatchCreateSchemaUriModel && $obj->getKey() > 0) {
                $obj->setAttr('has_use', BatchCreateSchemaUriModel::USE);
                if (! $obj->save()) {
                    return self::setError('保存失败');
                }
            }

            $model->delete();
        }

        return true;
    }

    /**
     * 设置直达链接视频队列.
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function setSchemaQueue(string $advId = '', string $materialId = ''): bool
    {
        $userId = KuaiShouAccountService::getKsUserId($advId);
        $list = (new MongoKsUserVideoModel())
            ->where('photo_name', new Regex('^' . $materialId, 'i'))
            ->where('user_id', $userId)
            ->where('new_status', MongoKsUserVideoModel::NEW_STATUS_NORMAL)
            ->whereIn('outer_loop_native', MongoKsUserVideoModel::NATIVE_NO)
            ->select();

        if (count($list) <= 0) {
            return self::setError('无数据');
        }

        $redis = Cache::store('redis')->handler();
        foreach ($list as $model) {
            $val = $advId . '|||' . $model->getAttr('_id');
            $redis->rPush(ConfigEnum::CACHE_CONFIG[KuaiShouEnum::BATCH_CONFIG_KEY]['batch_schema_video'], $val);
        }

        return true;
    }

    /**
     * 删除素材.
     * @param string $videoId 视频ID
     */
    private function delSchemaVideo(string $videoId = ''): bool
    {
        (new MongoKsSchemaVideoModel())
            ->where('account_id', $this->account->getKey())
            ->where('video_id', $videoId)
            ->delete();

        return true;
    }

    /**
     * 直达链接视频数据.
     * @param string $advId 账户ID
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function setMaterialVideo(string $advId = '', string $objectId = ''): bool
    {
        $this->account = AccountService::getAccountModel(0, $advId);
        if (! $this->account instanceof ClientAccountModel || ! $this->account->getKey()) {
            return self::setError('账户数据不存在');
        }
        $this->advUserId = strval(KuaiShouAccountService::getKsUserId($advId));

        $this->videoModel = (new MongoKsUserVideoModel())->where('_id', $objectId)->find();

        if (! $this->videoModel instanceof MongoKsUserVideoModel || ! $this->videoModel->getKey()) {
            return self::setError('数据不存在');
        }

        if ($this->videoModel->getAttr('new_status') != MongoKsUserVideoModel::NEW_STATUS_NORMAL || ! in_array($this->videoModel->getAttr('outer_loop_native'), MongoKsUserVideoModel::NATIVE_NO)) {
            $this->delSchemaVideo($this->videoModel->getAttr('photo_id'));
            return self::setError('物料已失效');
        }

        $photoArr = explode('-', $this->videoModel->getAttr('photo_name'));
        if (count($photoArr) <= 1) {
            return self::setError('数据不正确');
        }

        $materialId = trim($photoArr[0]);

        $this->uriModel = (new BatchCreateSchemaUriModel())
            ->where('advertiser_id', $advId)
            ->where('type', KuaiShouEnum::SCHEMA_VIDEO)
            ->whereRaw('BINARY material_id = :material_id', ['material_id' => $materialId])
            ->findOrEmpty();
        if (! $this->uriModel->getKey()) {
            return self::setError('物料ID不存在');
        }
        $this->videoModel->setAttr('is_material', KuaiShouEnum::VIDEO_MATERIAL_ENABLE);
        $this->videoModel->save();

        $this->schema = (new MongoKsSchemaVideoModel())
            ->where('account_id', $this->account->getKey())
            ->where('material_id', $materialId)
            ->where('video_id', $this->videoModel->getAttr('photo_id'))
            ->findOrEmpty();
        if (! $this->schema->getKey()) {
            $this->schema->setAttrs([
                'client_id' => $this->account->getAttr('client_id'),
                'account_id' => $this->account->getKey(),
                'adv_id' => $this->account->getAttr('advertiser_id'),
                'material_id' => $materialId,
                'video_id' => $this->videoModel->getAttr('photo_id'),
                'version_time' => time(),
                'video_type' => $this->videoModel->getAttr('width') > $this->videoModel->getAttr('height') ? KuaiShouEnum::CREATIVE_MATERIAL_TYPE_VIDEO_HORIZONTAL : KuaiShouEnum::CREATIVE_MATERIAL_TYPE_VIDEO_VERTICAL,
            ]);
        } else {
            $this->schema->setAttrs([
                'video_type' => $this->videoModel->getAttr('width') > $this->videoModel->getAttr('height') ? KuaiShouEnum::CREATIVE_MATERIAL_TYPE_VIDEO_HORIZONTAL : KuaiShouEnum::CREATIVE_MATERIAL_TYPE_VIDEO_VERTICAL,
            ]);
        }

        return $this->schema->save();
    }

    /**
     * 设置视频信息.
     * @throws DbException
     */
    private function setSchemaVideo(): bool
    {
        if (! $this->setVideoMaterialNum()) {
            return self::setError('设置素材数失败');
        }

        if (! $this->setVideoCoverUrl()) {
            return self::setError('更新封面ID失败');
        }

        return $this->saveVideoMaterial();
    }

    /**
     * 设置素材数.
     * @throws DbException
     */
    private function setVideoMaterialNum(): bool
    {
        $horizontalNum = Db::connect('mongodb')->name('ks_schema_video')
            ->master()
            ->where('account_id', $this->account->getKey())
            ->where('material_id', $this->uriModel->getAttr('material_id'))
            ->where('video_type', KuaiShouEnum::CREATIVE_MATERIAL_TYPE_VIDEO_HORIZONTAL)
            ->count();

        $verticalNum = Db::connect('mongodb')->name('ks_schema_video')
            ->master()
            ->where('account_id', $this->account->getKey())
            ->where('material_id', $this->uriModel->getAttr('material_id'))
            ->where('video_type', KuaiShouEnum::CREATIVE_MATERIAL_TYPE_VIDEO_VERTICAL)
            ->count();

        if ($this->schema->getAttr('video_type') == KuaiShouEnum::CREATIVE_MATERIAL_TYPE_VIDEO_HORIZONTAL) {
            $horizontalNum = $horizontalNum ?: 1;
        } else {
            $verticalNum = $verticalNum ?: 1;
        }
        $this->uriModel->setAttr('horizontal_material_num', $horizontalNum);
        $this->uriModel->setAttr('vertical_material_num', $verticalNum);
        $this->uriModel->setAttr('has_material', $horizontalNum + $verticalNum);
        return true;
    }

    /**
     * 更新封面ID.
     */
    private function setVideoCoverUrl(): bool
    {
        if ($this->schema instanceof MongoKsSchemaVideoModel && $this->schema->getKey()) {
            $this->uriModel->setAttr('material_object_id', strval($this->videoModel->getKey()));
        }

        return true;
    }

    /**
     * 保存素材信息.
     */
    private function saveVideoMaterial(): bool
    {
        (new BatchCreateSchemaUriModel())
            ->where('advertiser_id', $this->account->getAttr('advertiser_id'))
            ->where('type', KuaiShouEnum::SCHEMA_VIDEO)
            ->where('material_id', $this->uriModel->getAttr('material_id'))
            ->update([
                'vertical_material_num' => $this->uriModel->getAttr('vertical_material_num'),
                'horizontal_material_num' => $this->uriModel->getAttr('horizontal_material_num'),
                'has_material' => $this->uriModel->getAttr('has_material'),
                'material_object_id' => $this->uriModel->getAttr('material_object_id'),
                'update_time' => time(),
            ]);
        return true;
    }
}
